#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " "
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define IN_RDIR 0
#define OUT_RDIR 1
#define APPEND_RDIR 2
#define NONE -1
#define QUIT_CODE 44
char* filename;
 int rdir;

int lastcode=0;
char pwd[LINE_SIZE];
char myenv[LINE_SIZE];
char commandline[LINE_SIZE];
char* argv[ARGC_SIZE];
const char* getuser()
{
  return getenv("USER");
}
const char* gethostname1()
{
  return getenv("HOSTNAME");
}
void getpwd()
{
    getcwd(pwd,sizeof(pwd));
}

void check_rdir(char*cline)
{
  char* p=cline;
  while(*p)
  {
    if(*p=='<')
    {
      *p='\0';
      p+=1;
      while(isspace(*p))p++;
      filename=p;
      rdir=IN_RDIR;
      break;
    }
    else if(*p=='>')
    {
      *p++='\0';
      rdir=OUT_RDIR;
      if(*p=='>')
      {
        *p++='\0';
        rdir=APPEND_RDIR;
      }
      while(isspace(*p))p++;
      filename=p;
      break;
    }
    else{
      //do nothing
    }
    p++;
  }
}


void interact(char* cline,int size)
{
  getpwd();
  printf(LEFT"%s@%s %s"RIGHT" "LABLE,getuser(),gethostname1(),pwd);
  char* s=fgets(cline,size,stdin);
  assert(s);
  (void)s;
  //"aabcd\n\0" 回车不要
  commandline[strlen(commandline)-1]='\0';
  //printf("%s\n",commandline);
  
  //重定向：ls -l -a >> log.txt  / ls -l > log.txt  / cat < log.txt
  check_rdir(cline);
}
int splitstring(char*cline,char*_argv[])
{
  int i=0;
  _argv[i++]=strtok(cline,DELIM);
  while(_argv[i++]=strtok(NULL,DELIM));//第一个参数为NULL 表示接着上一次分割的地方分割
  return i-1;//可以计算_argv元素个数
}

int Buildcommand(char*_argv[],int argc)
{
  if(argc==2&&strcmp(_argv[0],"cd")==0)
  {
    chdir(_argv[1]);//更改当前工作目录为 _argv[1]
    //环境变量需要改
    getpwd();
    sprintf(getenv("PWD"),"%s",pwd);//改变后的工作目录写到PWD环境变量中
    return 1;
  }
  else if(argc==2&&strcmp(_argv[0],"echo")==0)
  {
    if(strcmp(_argv[1],"$?")==0)
    {
      printf("%d\n",lastcode);
      lastcode=0;
    }
    else if(*_argv[1]=='$')
    {
      char* ret=getenv(_argv[1]+1);
     if(ret) printf("%s\n",ret);
    }
    else{
      printf("%s\n",argv[1]);
    }
    return 1;
  }
else if(argc==2&&strcmp(_argv[0],"export")==0)
  {
    strcpy(myenv,_argv[1]);
    putenv(myenv);
    return 1;
  }
  if(strcmp(_argv[0],"ls")==0)
  {
    _argv[argc++]="--color";
    _argv[argc]=NULL;
  }
  return 0;
}
void normalcommand(char* _argv[])
{
  pid_t id=fork();
  if(id<0)
  {
    perror("fork");
    return ;
  }
  else if(id==0)
  {
    if(rdir==IN_RDIR)
    {
      //输入重定向                           //把从键盘输入重定向为从文件filename输入
      int fd=open(filename,O_RDONLY);
      dup2(fd,0);
    }
    else if(rdir==OUT_RDIR)
    {
      int fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666); //把内容从输出到屏幕重定向为输出到文件filename 且是“w”方式
      dup2(fd,1);
    }
    else if(rdir==APPEND_RDIR)
    {
      int fd=open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);// 把内容从输出到屏幕重定向为输出到文件filename 且是“a”方式
      dup2(fd,1);
    }
    
    execvp(_argv[0],_argv);
    exit(QUIT_CODE);
  }
  else{
    int status =0;
    pid_t ret_id=waitpid(id,&status,0);
    if(ret_id<0)
    {
      printf("等待失败！！\n");
    }
    else if(ret_id >0)
    {
      lastcode=WEXITSTATUS(status);
    }
  }
}
int main()
{
  int quit=0;
  while(!quit)
  {
    //1.
     rdir=NONE;
     filename=NULL;
  //2.交互
  interact(commandline,sizeof(commandline));
      //3. 分割字符串
     int argc= splitstring(commandline,argv);
     if(argc==0)continue;
     //4.判断命令是否为内建
    int n= Buildcommand(argv,argc);
    //5. 普通命令执行
    if(!n)normalcommand(argv);
    }
    return 0;
}
